/**
 * www.easyplatform.cn ©2016
 */
package cn.easyplatform.studio.web.views.impl;

import cn.easyplatform.lang.Strings;
import cn.easyplatform.studio.web.editors.EditorCallback;
import cn.easyplatform.studio.web.views.View;
import org.quartz.CronExpression;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.*;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

/**
 * @author <a href="mailto:shiny_vc@163.com">陈云亮</a> <br/>
 * @since 2.0.0 <br/>
 */
public class CronView implements View, EventListener<Event> {

	private EditorCallback<String> cb;

	// bandbox
	private Textbox second;

	private Textbox minute;

	private Textbox hour;

	private Textbox day;

	private Textbox month;

	private Textbox week;

	private Radiogroup secondGroup;

	private Radiogroup minuteGroup;

	private Radiogroup hourGroup;

	private Radiogroup dayGroup;

	private Radiogroup monthGroup;

	private Radiogroup weekGroup;

	private Spinner sfrom;

	private Spinner stime;

	private Spinner mfrom;

	private Spinner mtime;

	private Html executeTime;

	private Button btnOk;

	protected CronView(EditorCallback<String> cb) {
		this.cb = cb;
	}

	@Override
	public void doOverlapped() {
		Window win = createContent();
		win.setPosition("center");
		win.doOverlapped();
	}

	public void doOverlapped(String position) {
		Window win = createContent();
		win.setPosition(position);
		win.doOverlapped();
	}

	@Override
	public void doHighlighted() {
		Window win = createContent();
		win.setPosition("center");
		win.doHighlighted();
	}

	protected Window createContent() {
		Window win = new Window();
		win.setTitle(cb.getTitle());
		win.setClosable(true);
		win.setPage(cb.getPage());
		win.setMaximizable(true);
		win.setHeight("460px");
		win.setWidth("672px");
		win.setSizable(false);
		win.setFocus(true);
		Executions.createComponents("~./include/views/cron.zul", win, null);
		for (Component comp : win.getFellows()) {
			if (comp.getId().equals("second")) {
				second = (Textbox) comp;
			} else if (comp.getId().equals("minute")) {
				minute = (Textbox) comp;
			} else if (comp.getId().equals("hour")) {
				hour = (Textbox) comp;
			} else if (comp.getId().equals("day")) {
				day = (Textbox) comp;
			} else if (comp.getId().equals("month")) {
				month = (Textbox) comp;
			} else if (comp.getId().equals("week")) {
				week = (Textbox) comp;
			} else if (comp.getId().equals("secondGroup")) {
				secondGroup = (Radiogroup) comp;
				secondGroup.addEventListener(Events.ON_CHECK, this);
			} else if (comp.getId().equals("minuteGroup")) {
				minuteGroup = (Radiogroup) comp;
				minuteGroup.addEventListener(Events.ON_CHECK, this);
			} else if (comp.getId().equals("hourGroup")) {
				hourGroup = (Radiogroup) comp;
				hourGroup.addEventListener(Events.ON_CHECK, this);
			} else if (comp.getId().equals("dayGroup")) {
				dayGroup = (Radiogroup) comp;
				dayGroup.addEventListener(Events.ON_CHECK, this);
			} else if (comp.getId().equals("monthGroup")) {
				monthGroup = (Radiogroup) comp;
				monthGroup.addEventListener(Events.ON_CHECK, this);
			} else if (comp.getId().equals("weekGroup")) {
				weekGroup = (Radiogroup) comp;
				weekGroup.addEventListener(Events.ON_CHECK, this);
			} else if (comp.getId().equals("sfrom")) {
				sfrom = (Spinner) comp;
				sfrom.addEventListener(Events.ON_CHANGE, this);
			} else if (comp.getId().equals("stime")) {
				stime = (Spinner) comp;
				stime.addEventListener(Events.ON_CHANGE, this);
			} else if (comp.getId().equals("mfrom")) {
				mfrom = (Spinner) comp;
				mfrom.addEventListener(Events.ON_CHANGE, this);
			} else if (comp.getId().equals("mtime")) {
				mtime = (Spinner) comp;
				mtime.addEventListener(Events.ON_CHANGE, this);
			} else if (comp.getId().equals("executeTime")) {
				executeTime = (Html) comp;
			} else if (comp.getId().equals("btnOk")) {
				btnOk = (Button) comp;
				btnOk.addEventListener(Events.ON_CLICK, this);
			}
		}
		String[] expression = null;
		if (Strings.isBlank(cb.getValue()))
			expression = new String[] { "*", "*", "*", "*", "*", "?" };
		else
			expression = cb.getValue().split(" ");
		if (expression.length > 5) {
			second.setValue(expression[0]);
			minute.setValue(expression[1]);
			hour.setValue(expression[2]);
			day.setValue(expression[3]);
			month.setValue(expression[4]);
			week.setValue(expression[5]);
		}
		int[] value = null;
		if (second.getValue().equals("*") || second.getValue().equals("?"))
			secondGroup.setSelectedIndex(0);
		else if (second.getValue().indexOf("/") > 0) {
			secondGroup.setSelectedIndex(1);
			Iterator<Component> itr = secondGroup.queryAll("spinner")
					.iterator();
			String[] vals = second.getValue().split("/");
			while (itr.hasNext()) {
				Spinner sp = (Spinner) itr.next();
				if (itr.hasNext())
					sp.setValue(Integer.parseInt(vals[0]));
				else
					sp.setValue(Integer.parseInt(vals[1]));
				sp.setDisabled(false);
			}
		} else {
			String[] vals = second.getValue().split(",");
			value = new int[vals.length];
			for (int i = 0; i < vals.length; i++)
				value[i] = Integer.parseInt(vals[i]);
			secondGroup.setSelectedIndex(2);
		}
		Grid seconds = (Grid) secondGroup.query("grid");
		int count = 0;
		for (int i = 0; i < 4; i++) {
			Row row = new Row();
			for (int j = 0; j < 15; j++) {
				Checkbox cbx = new Checkbox(count + "");
				cbx.addEventListener(Events.ON_CHECK, this);
				cbx.setAttribute("root", secondGroup);
				cbx.setValue(count);
				if (value != null) {
					for (int k = 0; k < value.length; k++) {
						if (value[k] == count) {
							cbx.setChecked(true);
							break;
						}
					}
					cbx.setDisabled(false);
				} else
					cbx.setDisabled(true);
				row.appendChild(cbx);
				count++;
			}
			seconds.getRows().appendChild(row);
		}
		value = null;
		if (minute.getValue().equals("*") || minute.getValue().equals("?"))
			minuteGroup.setSelectedIndex(0);
		else if (minute.getValue().indexOf("/") > 0) {
			minuteGroup.setSelectedIndex(1);
			Iterator<Component> itr = minuteGroup.queryAll("spinner")
					.iterator();
			String[] vals = minute.getValue().split("/");
			while (itr.hasNext()) {
				Spinner sp = (Spinner) itr.next();
				if (itr.hasNext())
					sp.setValue(Integer.parseInt(vals[0]));
				else
					sp.setValue(Integer.parseInt(vals[1]));
				sp.setDisabled(false);
			}
		} else {
			String[] vals = minute.getValue().split(",");
			value = new int[vals.length];
			for (int i = 0; i < vals.length; i++)
				value[i] = Integer.parseInt(vals[i]);
			minuteGroup.setSelectedIndex(2);
		}
		Grid minutes = (Grid) minuteGroup.query("grid");
		count = 0;
		for (int i = 0; i < 4; i++) {
			Row row = new Row();
			for (int j = 0; j < 15; j++) {
				Checkbox cbx = new Checkbox(count + "");
				cbx.addEventListener(Events.ON_CHECK, this);
				cbx.setAttribute("root", minuteGroup);
				cbx.setValue(count);
				if (value != null) {
					for (int k = 0; k < value.length; k++) {
						if (value[k] == count) {
							cbx.setChecked(true);
							break;
						}
					}
					cbx.setDisabled(false);
				} else
					cbx.setDisabled(true);
				row.appendChild(cbx);
				count++;
			}
			minutes.getRows().appendChild(row);
		}
		value = null;
		if (hour.getValue().equals("*") || hour.getValue().equals("?"))
			hourGroup.setSelectedIndex(0);
		else if (hour.getValue().indexOf(",") > 0) {
			hourGroup.setSelectedIndex(1);
			String[] vals = hour.getValue().split(",");
			value = new int[vals.length];
			for (int i = 0; i < vals.length; i++)
				value[i] = Integer.parseInt(vals[i]);
		}
		Grid hours = (Grid) hourGroup.query("grid");
		count = 0;
		for (int i = 0; i < 2; i++) {
			Row row = new Row();
			if (i == 0)
				row.appendChild(new Label("AM:"));
			else
				row.appendChild(new Label("PM:"));
			for (int j = 0; j < 12; j++) {
				Checkbox cbx = new Checkbox(count + "");
				cbx.addEventListener(Events.ON_CHECK, this);
				cbx.setAttribute("root", hourGroup);
				cbx.setValue(count);
				if (value != null) {
					for (int k = 0; k < value.length; k++) {
						if (value[k] == count) {
							cbx.setChecked(true);
							break;
						}
					}
					cbx.setDisabled(false);
				} else
					cbx.setDisabled(true);
				row.appendChild(cbx);
				count++;
			}
			hours.getRows().appendChild(row);
		}
		value = null;
		if (day.getValue().equals("*") || day.getValue().equals("?"))
			dayGroup.setSelectedIndex(0);
		else if (!Strings.isBlank(day.getValue())) {
			dayGroup.setSelectedIndex(1);
			String[] vals = day.getValue().split(",");
			value = new int[vals.length];
			for (int i = 0; i < vals.length; i++)
				value[i] = Integer.parseInt(vals[i]);
		}
		Grid days = (Grid) dayGroup.query("grid");
		count = 0;
		for (int i = 0; i < 2; i++) {
			Row row = new Row();
			for (int j = 0; j < 12; j++) {
				count++;
				Checkbox cbx = new Checkbox(count + "");
				cbx.addEventListener(Events.ON_CHECK, this);
				cbx.setAttribute("root", dayGroup);
				cbx.setValue(count);
				if (value != null) {
					for (int k = 0; k < value.length; k++) {
						if (value[k] == count) {
							cbx.setChecked(true);
							break;
						}
					}
					cbx.setDisabled(false);
				} else
					cbx.setDisabled(true);
				row.appendChild(cbx);
			}
			if (i > 0) {
				Checkbox cbx = new Checkbox("31");
				cbx.addEventListener(Events.ON_CHECK, this);
				cbx.setAttribute("root", dayGroup);
				cbx.setValue(31);
				if (value != null) {
					for (int k = 0; k < value.length; k++) {
						if (value[k] == 31) {
							cbx.setChecked(true);
							break;
						}
					}
					cbx.setDisabled(false);
				} else
					cbx.setDisabled(true);
				row.appendChild(cbx);
			}
			days.getRows().appendChild(row);
		}
		value = null;
		if (month.getValue().equals("*") || month.getValue().equals("?"))
			monthGroup.setSelectedIndex(0);
		else if (!Strings.isBlank(month.getValue())) {
			monthGroup.setSelectedIndex(1);
			String[] vals = month.getValue().split(",");
			value = new int[vals.length];
			for (int i = 0; i < vals.length; i++)
				value[i] = Integer.parseInt(vals[i]);
		}
		Grid months = (Grid) monthGroup.query("grid");
		Row row = new Row();
		for (int i = 1; i <= 12; i++) {
			Checkbox cbx = new Checkbox(i + "");
			cbx.addEventListener(Events.ON_CHECK, this);
			cbx.setAttribute("root", monthGroup);
			cbx.setValue(i);
			if (value != null) {
				for (int k = 0; k < value.length; k++) {
					if (value[k] == i) {
						cbx.setChecked(true);
						break;
					}
				}
				cbx.setDisabled(false);
			} else
				cbx.setDisabled(true);
			row.appendChild(cbx);

		}
		months.getRows().appendChild(row);
		value = null;
		if (week.getValue().equals("*") || week.getValue().equals("?"))
			weekGroup.setSelectedIndex(0);
		else if (!Strings.isBlank(week.getValue())) {
			weekGroup.setSelectedIndex(1);
			String[] vals = week.getValue().split(",");
			value = new int[vals.length];
			for (int i = 0; i < vals.length; i++)
				value[i] = Integer.parseInt(vals[i]);
		}
		Grid weeks = (Grid) weekGroup.query("grid");
		row = new Row();
		for (int i = 1; i <= 7; i++) {
			Checkbox cbx = new Checkbox(i + "");
			cbx.addEventListener(Events.ON_CHECK, this);
			cbx.setAttribute("root", weekGroup);
			cbx.setValue(i);
			if (value != null) {
				for (int k = 0; k < value.length; k++) {
					if (value[k] == i) {
						cbx.setChecked(true);
						break;
					}
				}
				cbx.setDisabled(false);
			} else
				cbx.setDisabled(true);
			row.appendChild(cbx);

		}
		weeks.getRows().appendChild(row);
		return win;
	}

	@Override
	public void onEvent(Event evt) throws Exception {
		Component c = evt.getTarget();
		if (evt.getName().equals(Events.ON_CHECK)) {
			if (c instanceof Radio) {
				c = ((Radio) c).getRadiogroup();
				if (c == secondGroup) {
					if (secondGroup.getSelectedIndex() == 0) {
						second.setValue("*");
						setDisabled(secondGroup, 0);
					} else if (secondGroup.getSelectedIndex() == 1) {
						setDisabled(secondGroup, 2);
						second.setValue(sfrom.getValue() + "/"
								+ stime.getValue());
					} else {
						setDisabled(secondGroup, 1);
						second.setValue("0");
					}
				} else if (c == minuteGroup) {
					if (minuteGroup.getSelectedIndex() == 0) {
						minute.setValue("*");
						setDisabled(minuteGroup, 0);
					} else if (minuteGroup.getSelectedIndex() == 1) {
						setDisabled(minuteGroup, 2);
						minute.setValue(mfrom.getValue() + "/"
								+ mtime.getValue());
					} else {
						setDisabled(minuteGroup, 1);
						minute.setValue("0");
					}
				} else if (c == hourGroup) {
					if (hourGroup.getSelectedIndex() == 0) {
						hour.setValue("*");
						setDisabled(hourGroup, 0);
					} else {
						setDisabled(hourGroup, 1);
						hour.setValue("0");
					}
				} else if (c == dayGroup) {
					if (dayGroup.getSelectedIndex() == 0) {
						day.setValue("*");
						setDisabled(dayGroup, 0);
					} else {
						setDisabled(dayGroup, 1);
						day.setValue("1");
					}
				} else if (c == monthGroup) {
					if (monthGroup.getSelectedIndex() == 0) {
						month.setValue("*");
						setDisabled(monthGroup, 0);
					} else {
						setDisabled(monthGroup, 1);
						month.setValue("1");
					}
				} else if (c == weekGroup) {
					if (weekGroup.getSelectedIndex() == 0) {
						week.setValue("?");
						setDisabled(weekGroup, 0);
					} else {
						setDisabled(weekGroup, 1);
						week.setValue("1");
					}
				}
				btnOk.setDisabled(!validate());
			} else {// checkbox
				c = (Component) c.getAttribute("root");
				Iterator<Component> itr = c.queryAll("checkbox").iterator();
				StringBuilder sb = new StringBuilder();
				while (itr.hasNext()) {
					Checkbox cbx = (Checkbox) itr.next();
					if (cbx.isChecked())
						sb.append((int)cbx.getValue()).append(",");
				}
				if (sb.length() > 1)
					sb.deleteCharAt(sb.length() - 1);
				if (c == secondGroup)
					second.setValue(sb.toString());
				else if (c == minuteGroup)
					minute.setValue(sb.toString());
				else if (c == hourGroup)
					hour.setValue(sb.toString());
				else if (c == dayGroup)
					day.setValue(sb.toString());
				else if (c == monthGroup)
					month.setValue(sb.toString());
				else if (c == weekGroup)
					week.setValue(sb.toString());
			}
			btnOk.setDisabled(!validate());
		} else if (c instanceof Spinner) {
			if (c == sfrom || c == stime) {
				second.setValue(sfrom.getValue() + "/" + stime.getValue());
			} else if (c == mfrom || c == mtime) {
				minute.setValue(mfrom.getValue() + "/" + mtime.getValue());
			}
			btnOk.setDisabled(!validate());
		} else if (c instanceof Button) {
			if (validate()) {
				StringBuilder sb = new StringBuilder();
				sb.append(second.getValue()).append(" ")
						.append(minute.getValue()).append(" ")
						.append(hour.getValue()).append(" ")
						.append(day.getValue()).append(" ")
						.append(month.getValue()).append(" ")
						.append(week.getValue());
				cb.setValue(sb.toString());
				btnOk.getRoot().detach();
			}
		}
	}

	private boolean validate() {
		StringBuilder sb = new StringBuilder();
		sb.append(second.getValue()).append(" ").append(minute.getValue())
				.append(" ").append(hour.getValue()).append(" ")
				.append(day.getValue()).append(" ").append(month.getValue())
				.append(" ").append(week.getValue());
		try {
			CronExpression exp = new CronExpression(sb.toString());
			sb.setLength(0);
			SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
			Date now = new Date();
			sb.setLength(0);
			for (int i = 0; i < 5; i++) {
				now = exp.getNextValidTimeAfter(now);
				sb.append("<div style=\"padding: 3px 10px\">")
						.append("<span style=\"margin-right:10px\">")
						.append(i + 1).append(":</span><span>")
						.append(df.format(now)).append("</span></div>");
			}
			executeTime.setContent(sb.toString());
			sb = null;
			return true;
		} catch (Exception ex) {
			executeTime.setContent(null);
			return false;
		}
	}

	private void setDisabled(Component c, int state) {
		Iterator<Component> itr = c.queryAll("checkbox").iterator();
		while (itr.hasNext()) {
			Checkbox cbx = (Checkbox) itr.next();
			cbx.setDisabled(state != 1);
			Integer val = cbx.getValue();
			if (!cbx.isDisabled()) {
				if ((cbx.getPreviousSibling() == null || (!(cbx
						.getPreviousSibling() instanceof Checkbox)))
						&& (val == 1 || val == 0))
					cbx.setChecked(true);
				else
					cbx.setChecked(false);
			}
		}
		itr = c.queryAll("spinner").iterator();
		while (itr.hasNext()) {
			Spinner cbx = (Spinner) itr.next();
			cbx.setDisabled(state != 2);
		}
	}
}
